package com.nx.platform.es.bean.dto;


import com.google.common.base.Preconditions;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableMap;
import com.nx.platform.es.bean.modle.param.ParamField;
import com.nx.platform.es.bean.modle.query.QueryField;
import com.nx.platform.es.bean.modle.score.ScoreFunctionField;
import com.nx.platform.es.bean.modle.sort.SortField;
import com.nx.platform.es.common.utils.MoreSplitters;
import com.nx.platform.es.bean.modle.agg.AggField;
import com.nx.platform.es.bean.modle.rescore.RescoreField;
import com.nx.platform.es.common.utils.MoreMaps;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.elasticsearch.common.unit.TimeValue;

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author
 * @since 2016年10月15日
 */
public class ESQueryConfDTO {

    private Set<String> cluster;
    private String[] index;
    private String[] type;
    private String[] fetch;
    private TimeValue timeout;
    private List<QueryField> queryFields;
    private List<AggField> aggFields;
    private Map<String, OrderStrategy> orderStrategys;
    private int expireAfterWrite;
    private boolean useRequestCache;

    public static ESQueryConfDTO create(Map<?, ?> settings) {
        ESQueryConfDTO config = new ESQueryConfDTO();
        config.cluster = MoreMaps.getStringSet(settings, "cluster", MoreSplitters.COMMA);
        config.index = MoreMaps.getStringArray(settings, "index", MoreSplitters.COMMA);
        config.type = MoreMaps.getStringArray(settings, "type", MoreSplitters.COMMA);
        config.timeout = TimeValue.parseTimeValue(MoreMaps.getString(settings, "timeout"),
                TimeValue.timeValueMillis(500), "timeout");
        config.fetch = MoreMaps.getStringArray(settings, "fetch", MoreSplitters.COMMA);
        config.expireAfterWrite = MoreMaps.getIntValue(settings, "expireAfterWrite", 10);
        config.useRequestCache = MoreMaps.getBooleanValue(settings, "useRequestCache");

        List<Map<?, ?>> queryFieldList = MoreMaps.getObject(settings, "query_fields");
        Preconditions.checkState(CollectionUtils.isNotEmpty(queryFieldList), "query_fields empty or null");
        config.queryFields = queryFieldList.stream().map(QueryField::create).collect(Collectors.toList());

        List<Map<?, ?>> aggFieldList = MoreMaps.getObject(settings, "agg_fields");
        if (CollectionUtils.isNotEmpty(aggFieldList)) {
            config.aggFields = aggFieldList.stream().map(AggField::create).collect(Collectors.toList());
        }

        Map<String, Map<?, ?>> orderStrategySettingMap = MoreMaps.getObject(settings, "order_strategys");
        if (MapUtils.isNotEmpty(orderStrategySettingMap)) {
            ImmutableMap.Builder<String, OrderStrategy> orderStrategys = ImmutableMap.builder();
            for (String strategy : orderStrategySettingMap.keySet()) {
                Map<?, ?> orderStrategySetting = MoreMaps.getObject(orderStrategySettingMap, strategy);
                OrderStrategy orderStrategy = new OrderStrategy();
                orderStrategy.scoreMode = MoreMaps.getString(orderStrategySetting, "scoreMode", "sum");
                orderStrategy.boostMode = MoreMaps.getString(orderStrategySetting, "boostMode", "sum");
                orderStrategy.geohashLevel = MoreMaps.getIntValue(orderStrategySetting, "geohashLevel", 5);
                orderStrategy.expireAfterWrite = MoreMaps.getIntValue(orderStrategySetting, "expireAfterWrite", 60);
                orderStrategy.useRequestCache = MoreMaps.getBooleanValue(orderStrategySetting, "useRequestCache");
                List<Map<?, ?>> params = MoreMaps.getObject(orderStrategySetting, "params");
                if (CollectionUtils.isNotEmpty(params)) {
                    orderStrategy.paramFields = params.stream().map(ParamField::create).collect(Collectors.toList());
                }
                List<Map<?, ?>> sorts = MoreMaps.getObject(orderStrategySetting, "sorts");
                if (CollectionUtils.isNotEmpty(sorts)) {
                    orderStrategy.sortFields = sorts.stream().map(SortField::create).collect(Collectors.toList());
                }
                List<Map<?, ?>> scores = MoreMaps.getObject(orderStrategySetting, "scores");
                if (CollectionUtils.isNotEmpty(scores)) {
                    orderStrategy.scoreFunctionFields = scores.stream().map(ScoreFunctionField::create).collect(Collectors.toList());
                }
                List<Map<?, ?>> rescorers = MoreMaps.getObject(orderStrategySetting, "rescorers");
                if (CollectionUtils.isNotEmpty(rescorers)) {
                    orderStrategy.rescoreFields = rescorers.stream().map(RescoreField::create).collect(Collectors.toList());
                }
                for (String s : Splitter.on(',').trimResults().omitEmptyStrings().split(strategy)) {
                    orderStrategys.put(s, orderStrategy);
                }
            }
            config.orderStrategys = orderStrategys.build();
        }
        config.validate();
        return config;
    }

    private void validate() {
        Preconditions.checkState(index != null && index.length > 0, "index null");
        Preconditions.checkState(type != null && type.length > 0, "type null");
        Preconditions.checkState(timeout != null && timeout.getSeconds() < 10, "timeout null or incorrect");
        Preconditions.checkState(queryFields != null && !queryFields.isEmpty(), "queryFields null or empty");
    }

    public Set<String> getCluster() {
        return cluster;
    }

    public String[] getIndex() {
        return ArrayUtils.clone(index);
    }

    public String[] getType() {
        return ArrayUtils.clone(type);
    }

    public String[] getFetch() {
        return ArrayUtils.clone(fetch);
    }

    public TimeValue getTimeout() {
        return timeout;
    }

    public List<QueryField> getQueryFields() {
        return queryFields;
    }

    public List<AggField> getAggFields() {
        return aggFields;
    }

    public Map<String, OrderStrategy> getOrderStrategys() {
        return orderStrategys;
    }

    public int getExpireAfterWrite() {
        return expireAfterWrite;
    }

    public boolean isUseRequestCache() {
        return useRequestCache;
    }

    public static class OrderStrategy {

        private List<ParamField> paramFields;
        private List<SortField> sortFields;
        private List<ScoreFunctionField> scoreFunctionFields;
        // 打分函数与Query得分如何结合 multiply、replace、sum、avg、max、min
        private String boostMode;
        // 打分函数之间如何结合 multiply、sum、avg、first、max、min
        private String scoreMode;
        // 5 ±2.4km; 6 ±0.61km;
        private int geohashLevel;
        private List<RescoreField> rescoreFields;
        // 过期时间(秒)
        private int expireAfterWrite;
        private boolean useRequestCache;

        public String getBoostMode() {
            return boostMode;
        }

        public String getScoreMode() {
            return scoreMode;
        }

        public int getGeohashLevel() {
            return geohashLevel;
        }

        public List<ParamField> getParamFields() {
            return paramFields;
        }

        public List<SortField> getSortFields() {
            return sortFields;
        }

        public List<ScoreFunctionField> getScoreFunctionFields() {
            return scoreFunctionFields;
        }

        public List<RescoreField> getRescoreFields() {
            return rescoreFields;
        }

        public int getExpireAfterWrite() {
            return expireAfterWrite;
        }

        public boolean isUseRequestCache() {
            return useRequestCache;
        }

    }

}
